package com.woniu.zsd.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.woniu.dto.LogUserDto;
import com.woniu.form.LoginByPhoneForm;
import com.woniu.form.LoginByUsernameForm;
import com.woniu.form.UpdatePhoneForm;
import com.woniu.form.UserRegisterForm;
import com.woniu.zsd.commons.exception.ZsdException;
import com.woniu.zsd.user.entity.User;
import com.woniu.zsd.user.entity.UserInfo;
import com.woniu.zsd.user.mapper.UserInfoMapper;
import com.woniu.zsd.user.mapper.UserMapper;
import com.woniu.zsd.user.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.commons.util.RedisKey;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.redisson.api.RLock;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-27
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Override
    public void sendRegisterCode(String phone) {
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisKey.REGISTER_LAST_SEND_KEY(phone)))) {
            throw new RuntimeException("验证码已发送，请稍后再试");
        }
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("phone", phone);

        User user = userMapper.selectOne(wrapper);
        if (user != null) {
            throw new RuntimeException("该手机号已注册");
        }
        rabbitTemplate.convertAndSend("register-code-queue", phone);
    }

    @Override
    public void sendLoginCode(String phone) {
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisKey.REGISTER_LAST_SEND_KEY(phone)))) {
            throw new RuntimeException("验证码已发送，请稍后再试");
        }
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("phone", phone);

        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new RuntimeException("手机号不存在");
        }
        rabbitTemplate.convertAndSend("register-code-queue", phone);
    }

    @Override
    public User updateUserPhone(UpdatePhoneForm upf,String id) {
        // 验证验证码是否正确
        String redisCode = stringRedisTemplate.opsForValue().get(RedisKey.PHONE_KEY(upf.getPhone()));
        if (!Objects.equals(redisCode, upf.getCode())) {
            throw new ZsdException("验证码错误", 20003);
        }

        User user = userMapper.selectById(id);
        user.setPhone(upf.getPhone());
        userMapper.updateById(user);
        return user;
    }

    @Override
    public void banUser(String id) {
        // 查询用户当前状态
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new ZsdException("用户不存在", 20001); // 用户不存在时抛出异常
        }
        // 判断用户当前状态
        if (user.getStatus() == 1) {
            // 如果用户是正常状态，则封禁用户
            userMapper.update(null, new UpdateWrapper<User>().set("status", 0).eq("id", id));
        } else {
            // 如果用户已经是封禁状态，无需再次封禁
            userMapper.update(null,new UpdateWrapper<User>().set("status",1).eq("id",id));
        }
    }

    @Override
    public void register(UserRegisterForm userRegisterForm)throws Exception{
        // 验证码校验
        if (!Objects.equals(stringRedisTemplate.opsForValue().get(RedisKey.PHONE_KEY(userRegisterForm.getPhone())), userRegisterForm.getCode())) {
            throw new ZsdException("验证码错误",20000);
        }

        // 获取分布式锁
        RLock phoneLock = redissonClient.getLock("lock:register:phone:" + userRegisterForm.getPhone());
        RLock usernameLock = redissonClient.getLock("lock:register:username:" + userRegisterForm.getUsername());
        RLock lock = redissonClient.getMultiLock(phoneLock);

        try {
            // 尝试获取锁
            if (!lock.tryLock(10, TimeUnit.SECONDS)) {
                throw new ZsdException("服务器繁忙，请稍后再试",20001);
            }

            // 检查手机号是否已被注册
            QueryWrapper<User> phoneWrapper = new QueryWrapper<>();
            phoneWrapper.eq("phone", userRegisterForm.getPhone());
            if (userMapper.selectOne(phoneWrapper) != null) {
                throw new Exception("手机号已被注册");
            }

            QueryWrapper<UserInfo> loginNameWrapper = new QueryWrapper<>();
            loginNameWrapper.eq("username", userRegisterForm.getUsername());
            if (userInfoMapper.selectOne(loginNameWrapper) != null) {
                throw new Exception("用户名已被注册");
            }
            System.out.println(userRegisterForm);

            // 使用 BeanUtil 进行属性拷贝
            User user = new User();
            UserInfo userInfo = new UserInfo();
            BeanUtils.copyProperties(userRegisterForm,user);
            BeanUtils.copyProperties(userRegisterForm,userInfo);
            user.setRegisterTime(String.valueOf((LocalDateTime.now())));
            user.setStatus(0);
            userInfo.setPoint(600);
            user.setStatus(1);

            // 插入用户信息到sysRegularUser表
            userMapper.insert(user);
            userInfoMapper.insert(userInfo);
            userInfoMapper.update(userInfo.setUserId(userInfo.getId()), new UpdateWrapper<UserInfo>().eq("id", userInfo.getId()));
        }finally {
            // 释放锁
            lock.unlock();
        }
    }

    @Override
    public LogUserDto loginByPhoneAndCode(LoginByPhoneForm lbpf) {
        QueryWrapper<User> userWrapper = new QueryWrapper<>();
        userWrapper.eq("phone", lbpf.getPhone());
        User user = userMapper.selectOne(userWrapper);

        if (user == null) {
            throw new ZsdException("用户不存在", 20001);
        }

        // 验证验证码是否正确
        String redisCode = stringRedisTemplate.opsForValue().get(RedisKey.PHONE_KEY(lbpf.getPhone()));
        if (!Objects.equals(redisCode, lbpf.getCode())) {
            throw new ZsdException("验证码错误", 20003);
        }

        // 用于保存用户信息
        UserInfo userInfo = userInfoMapper.selectById(user.getId());

        // 判断用户状态
        if (user.getStatus().equals(0)) {
            throw new ZsdException("用户已被封禁", 20001);
        }

        UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", userInfo.getId())
                .set("log_time", String.valueOf(LocalDateTime.now()));
        userInfoMapper.update(null, updateWrapper);
        // 返回用户信息
        LogUserDto dto = new LogUserDto();
        BeanUtils.copyProperties(userInfo, dto);
        return dto;
    }

    @Override
    public LogUserDto loginByUsernameAndPassword(LoginByUsernameForm lbuf) {
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("username", lbuf.getUsername());
        UserInfo userInfo = userInfoMapper.selectOne(wrapper);

        if (userInfo == null) {
            throw new ZsdException("用户名不存在", 20001);
        }

        // 验证密码是否正确
        if (!userInfo.getPassword().equals(lbuf.getPassword())) {
            throw new ZsdException("密码错误", 20002);
        }

        // 用于查询用户状态
        User user = userMapper.selectById(userInfo.getUserId());

        // 判断用户状态
        if (user.getStatus().equals(0)) {
            throw new ZsdException("用户已被封禁", 20001);
        }

        UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", userInfo.getId())
                .set("log_time", String.valueOf(LocalDateTime.now()));
        userInfoMapper.update(null, updateWrapper);
        // 返回用户信息
        LogUserDto dto = new LogUserDto();
        BeanUtils.copyProperties(userInfo, dto);
        return dto;
    }
}
